//**********************************************************************
//	Programmer	-	Sujal Bista
//	File Name	-	graphicsUtility.h
//	Description	:	
//					Graphics Utility
//**********************************************************************

#ifndef _GRAPHICS_UTILITY_H_
#define _GRAPHICS_UTILITY_H_ 

#define _USE_DIRECTX_
//#define _USE_OPENGL_

#ifdef _USE_DIRECTX_
	#include "graphicsDX.h"
#endif
#ifdef _USE_OPENGL_
	#include "graphicsGL.h"
#endif
#include "camera.h"

//few definations that will allow us to switch between directX or openGL
#ifdef _USE_DIRECTX_
	#define graphics graphicsDX
	#define device graphicsDX::deviceD3D
	#define deviceContext graphicsDX::deviceContextD3D
	#define buffer graphicsDX::bufferD3D
	#define texture1D graphicsDX::texture1DD3D
	#define texture2D graphicsDX::texture2DD3D
	#define texture3D graphicsDX::texture3DD3D
	#define blob graphicsDX::blobD3D
	#define inputLayout graphicsDX::inputLayoutD3D
	#define renderTargetView graphicsDX::renderTargetViewD3D
	#define depthStencilView graphicsDX::depthStencilViewD3D
	#define shaderResourceView graphicsDX::shaderResourceViewD3D
	#define unorderedAccessView graphicsDX::unorderedAccessViewD3D
	#define shaderVertex graphicsDX::shaderVertexD3D
	#define shaderHull graphicsDX::shaderHullD3D
	#define shaderDomain graphicsDX::shaderDomainD3D
	#define shaderGeometry graphicsDX::shaderGeometryD3D
	#define shaderPixel graphicsDX::shaderPixelD3D
	#define shaderCompute graphicsDX::shaderComputeD3D
	#define samplerState graphicsDX::samplerStateD3D
	#define rasterizerState graphicsDX::rasterizerStateD3D
	#define commandList graphicsDX::commandListD3D
#endif

//few definations that will allow us to switch between directX or openGL
#ifdef _USE_OPENGL_
	#define graphics graphicsGL
	#define device graphicsGL::deviceGL
	#define deviceContext graphicsGL::deviceContextGL
	#define buffer graphicsGL::bufferGL
	#define texture1D graphicsGL::texture1DGL
	#define texture2D graphicsGL::texture2DGL
	#define texture3D graphicsGL::texture3DGL
	//#define resource graphicsGL::resourceGL
	#define blob graphicsGL::blobGL
	#define inputLayout graphicsGL::inputLayoutGL
	#define renderTargetView graphicsGL::renderTargetViewGL
	#define depthStencilView graphicsGL::depthStencilViewGL
	#define shaderResourceView graphicsGL::shaderResourceViewGL
	#define unorderedAccessView graphicsGL::unorderedAccessViewGL
	#define shaderVertex graphicsGL::shaderVertexGL
	#define shaderHull graphicsGL::shaderHullGL
	#define shaderDomain graphicsGL::shaderDomainGL
	#define shaderGeometry graphicsGL::shaderGeometryGL
	#define shaderPixel graphicsGL::shaderPixelGL
	#define shaderCompute graphicsGL::shaderComputeGL
	#define samplerState graphicsGL::samplerStateGL
	#define rasterizerState graphicsGL::rasterizerStateGL
	#define commandList graphicsGL::commandListGL
#endif





#include "bucketList.h"
#include "utilityFunctions.h"

class graphicsUtility
{

		enum BASIC_FONT_TYPE
		{
			BASIC_FONT_TYPE_ASCII=0,
			BASIC_FONT_TYPE_END
		};

		enum BASIC_SHADER_TYPE
		{
			BASIC_SHADER_TYPE_COLOR=0,	//shader that just colors
			BASIC_SHADER_TYPE_COLOR_NORMAL,	//shader that just uses color and light
			BASIC_SHADER_TYPE_COLOR_TEXTURE,	//shader that just uses color and texture
			BASIC_SHADER_TYPE_COLOR_NORMAL_TEXTURE,	//shader that uses color, light and texture
			BASIC_SHADER_TYPE_TEXTURE_2D,	//shader that just colors
			BASIC_SHADER_TYPE_END
		};

		//variables for color shader
		struct basicColorShaderVariables
		{
			unsigned int shaderBufferIndex;
		} basicCSVariable;

		//variables for color normal shader
		struct basicColorNormalShaderVariables
		{
			unsigned int shaderBufferIndex;
		} basicCNSVariable;

		//variables for color texture shader
		struct basicColorTextureShaderVariables
		{
			unsigned int shaderBufferIndex;
			unsigned int textureIndex;
		} basicCTSVariable;

		//variables for color texture shader
		struct basicColorTexture2DShaderVariables
		{
			unsigned int shaderBufferIndex;
			unsigned int textureIndex;
		} basicCT2DSVariable;

		//variables for color normal texture shader
		struct basicColorNormalTextureShaderVariables
		{
			unsigned int shaderBufferIndex;
			unsigned int textureIndex;
		} basicCNTSVariable;

	public:

		//vertex data with position
		struct vertexP
		{

			float3 position;
			vertexP(void){};

			vertexP(const float3 &position)
			{
				this->position=position;
			}
		};

		//vertex data with position and normal
		struct vertexPN
		{

			float3 position;
			float3 normal;

			vertexPN(void){};

			vertexPN(const float3 &position, const float3 &normal)
			{
				this->position=position;
				this->normal=normal;
			}
		};

		//vertex data with position and texture coordinate
		struct vertexPT
		{
			float3 position;
			float2 texCoord;

			vertexPT(void){};

			vertexPT(const float3 &position, const float2 &texCoord)
			{
				this->position=position;
				this->texCoord=texCoord;
			}
		};	

		//verted data with position, normal and texture coordinate
		struct vertexPNT
		{
			float3 position;
			float3 normal;
			float2 texCoord;

			vertexPNT(void){};

			vertexPNT(const float3 &position, const float3 &normal, const float2 &texCoord)
			{
				this->position=position;
				this->normal=normal;
				this->texCoord=texCoord;
			}
		};

		//structure for light
		struct light
		{
			float4 position;
			float4 direction;
			float4 color;
			float4 falloff; // x = dist end, y = dist range, z = cos angle end (outer cone), w = cos range (inner cone)
			float4x4 modelViewProj; //view projection matrix of the light
		};

		//structure used by basic color
		struct shaderBufferColor
		{
			float4x4 worldMatrix;
			float4x4 viewMatrix; 
			float4x4 projMatrix;
			float4 color;
		};

		//structure used by basic color 2D
		struct shaderBufferColor2D
		{
			float4 dimension;
			float4 color;
		};

		//structure used by basic color and light
		struct shaderBufferColorNormal
		{
			float4x4 worldMatrix;
			float4x4 viewMatrix; 
			float4x4 projMatrix;
			float4 color;
			light lightData;
		};

		//structure of the font
		struct font
		{

			//stores info of character
			struct charInfo
			{
				unsigned int width;
				unsigned int height;
				int startX;
				int startY;
				int translateAmount;

			};

			//details of the characters
			charInfo charDetails[256];

			//texture used for the font
			shaderResourceView fontTextureSRV;
			samplerState  fontTextureSampler;

			//dimension of the font texture
			unsigned int imageWidth,imageHeight;
			
			//font size
			int size;

			//dimension of individual size
			int characterWidth, characterHeight;

			//total character per row,col in the texture
			int charPerRow,charPerCol;	

			//color of the font
			float4 color;
		};

	private:
		
		graphics gl;	//graphics
		device gDevice;	//graphics device
		deviceContext dContext;	//graphics device context
		texture2D rTarget;	//render target
		texture2D dStencil;	//depth stencil
		renderTargetView rTargetView;	//render traget view
		depthStencilView dStencilView;	//depth stencil view

		//shader that just colors
		shaderVertex *basicVShaders;
		shaderPixel *basicPShaders;

		//vertex layouts
		inputLayout *vertexLayouts;

		//shader buffers
		buffer *shaderBuffers;
		
		//fonts
		font *fonts;

		//basic rasterizer mode
		rasterizerState solidStateMode;	//filled state
		rasterizerState wireFrameStateMode; //wire state
		
	public:

		//default constructor
		graphicsUtility(void);

		//initilizes the utility
		bool init(HWND &hWnd,const unsigned int &width, const unsigned int &height);

		//init fonts
		bool loadFonts(deviceContext &dContext);

		//init shaders
		bool loadShaders(deviceContext &dContext);
					
		//creates blank 2D texture
		bool createTexture2D(const unsigned int &width, const unsigned int &height, const graphicsDefinitions::DATA_FORMAT &format,texture2D &text2D);
	
		//updates 2D texture
		bool updateTexture2D(deviceContext &dContext, texture2D &text2D, const unsigned int &startX, const unsigned int &startY, const unsigned int &width, const unsigned int &height, void *data, const unsigned int &rowSize,const unsigned int &dataSize);


		//creates vertex buffers and index buffer
		template<typename T,typename U> bool createVertexBuffers(deviceContext &dContext, const bucketList<T> &vertexData, bucketList<U> &indexData, buffer &vertexBuffer, buffer &indexBuffer)
		{
			unsigned int rowPitch;
			unsigned int depthPitch;

			//buffer desc
			graphics::bufferDesc desc;
			desc.byteWidth=sizeof(T)*vertexData.size();
			desc.usage=graphicsDefinitions::USAGE_DYNAMIC;
			desc.bindFlags=graphicsDefinitions::BIND_FLAG_VERTEX_BUFFER;
			desc.cpuAccessFlags=graphicsDefinitions::CPU_ACCESS_FLAG_WRITE;
			desc.miscFlags=graphicsDefinitions::RESOURCE_MISC_FLAG_NONE;
			desc.structureByteStride=0;

			//create buffer in GPU
			if(!gDevice.createBuffer(desc,NULL,0,0,&vertexBuffer))
				return false;

			//map the buffer
			T *vData=(T *)dContext.map(&vertexBuffer,0,graphicsDefinitions::MAP_WRITE_DISCARD,rowPitch,depthPitch);
			if(!vData)
				return false;

			//copy the data
			for(unsigned int i=0;i<vertexData.size();i++)
				vData[i]=vertexData[i];
			//unmap the buffer
			dContext.unmap(&vertexBuffer,0);

			
			//buffer desc
			desc.byteWidth=sizeof(U)*indexData.size();
			desc.usage=graphicsDefinitions::USAGE_DYNAMIC;
			desc.bindFlags=graphicsDefinitions::BIND_FLAG_INDEX_BUFFER;
			desc.cpuAccessFlags=graphicsDefinitions::CPU_ACCESS_FLAG_WRITE;
			desc.miscFlags=graphicsDefinitions::RESOURCE_MISC_FLAG_NONE;
			desc.structureByteStride=0;

			//create buffer in GPU
			if(!gDevice.createBuffer(desc,NULL,0,0,&indexBuffer))
			{
				vertexBuffer.clear();
				return false;
			}

			//map the buffer
			U *iData=(U *)dContext.map(&indexBuffer,0,graphicsDefinitions::MAP_WRITE_DISCARD,rowPitch,depthPitch);
			if(!iData)
			{
				vertexBuffer.clear();
				return false;
			}
			//copy the data
			for(unsigned int i=0;i<indexData.size();i++)
				iData[i]=indexData[i];
			//unmap the buffer
			dContext.unmap(&indexBuffer,0);
		
			return true;
		}
		//creates vertex buffers
		template<typename T> bool createVertexBuffers(deviceContext &dContext, const bucketList<T> &vertexData, buffer &vertexBuffer)
		{
			unsigned int rowPitch;
			unsigned int depthPitch;

			//create new vertex buffer
			//buffer desc
			graphics::bufferDesc desc;
			desc.byteWidth=sizeof(T)*vertexData.size();
			desc.usage=graphicsDefinitions::USAGE_DYNAMIC;
			desc.bindFlags=graphicsDefinitions::BIND_FLAG_VERTEX_BUFFER;
			desc.cpuAccessFlags=graphicsDefinitions::CPU_ACCESS_FLAG_WRITE;
			desc.miscFlags=graphicsDefinitions::RESOURCE_MISC_FLAG_NONE;
			desc.structureByteStride=0;

			//create buffer in GPU
			if(!gDevice.createBuffer(desc,NULL,0,0,&vertexBuffer))
				return false;

			//map the buffer
			T *vData=(T *)dContext.map(&vertexBuffer,0,graphicsDefinitions::MAP_WRITE_DISCARD,rowPitch,depthPitch);
			if(!vData)
				return false;

			//copy the data
			for(unsigned int i=0;i<vertexData.size();i++)
				vData[i]=vertexData[i];
			//unmap the buffer
			dContext.unmap(&vertexBuffer,0);
		
			return true;
		}
		
		//uses basic shader to draw
		bool drawColor(deviceContext &dContext,buffer &vertexBuffer,buffer &indexBuffer,const graphicsDefinitions::PRIMITIVE_TOPOLOGY &topology,const float4x4 &worldMatrix,const float4x4 &viewMatrix, const float4x4 &projMatrix,const float4 &color,const unsigned int &indexCount,const unsigned int &startIndexLocation,const int &baseVertexLocation);
		//uses basic shader to draw
		bool drawColor(deviceContext &dContext,buffer &vertexBuffer,const graphicsDefinitions::PRIMITIVE_TOPOLOGY &topology,const float4x4 &worldMatrix,const float4x4 &viewMatrix, const float4x4 &projMatrix,const float4 &color,const unsigned int &vertexCount,const unsigned int &startVertexLocation);
		//uses basic shader to draw
		template<typename T> bool drawColor(deviceContext &dContext,const bucketList<T> &vertexData,const graphicsDefinitions::PRIMITIVE_TOPOLOGY &topology,const float4x4 &worldMatrix,const float4x4 &viewMatrix, const float4x4 &projMatrix,const float4 &color)
		{
			buffer vertexBuffer;

			//creates vertex buffers
			if(!createVertexBuffers(dContext,vertexData,vertexBuffer))
				return false;
				
			return drawColor(dContext,vertexBuffer,topology,worldMatrix,viewMatrix,projMatrix,color,vertexData.size(),0);
		}

		//uses basic shader to draw
		bool drawColorNormal(deviceContext &dContext,buffer &vertexBuffer, buffer &indexBuffer,const graphicsDefinitions::PRIMITIVE_TOPOLOGY &topology,const float4x4 &worldMatrix,const float4x4 &viewMatrix, const float4x4 &projMatrix,const float4 &color,const light &lightData,const unsigned int &indexCount,const unsigned int &startIndexLocation,const int &baseVertexLocation);
		//uses basic shader to draw
		bool drawColorNormal(deviceContext &dContext,buffer &vertexBuffer,const graphicsDefinitions::PRIMITIVE_TOPOLOGY &topology,const float4x4 &worldMatrix,const float4x4 &viewMatrix, const float4x4 &projMatrix,const float4 &color,const light &lightData,const unsigned int &vertexCount,const unsigned int &startVertexLocation);
		//uses basic shader to draw
		template<typename T> bool drawColorNormal(deviceContext &dContext,const bucketList<T> &vertexData,const graphicsDefinitions::PRIMITIVE_TOPOLOGY &topology,const float4x4 &worldMatrix,const float4x4 &viewMatrix, const float4x4 &projMatrix,const float4 &color,const light &lightData)
		{
			buffer vertexBuffer;

			//creates vertex buffers
			if(!createVertexBuffers(dContext,vertexData,vertexBuffer))
				return false;

			//show the buffer
			return drawColorNormal(dContext,vertexBuffer,topology,worldMatrix,viewMatrix,projMatrix,color,lightData,vertexData.size(),0);
		}
				
		//uses basic shader to draw
		bool drawColorTexture(deviceContext &dContext,buffer &vertexBuffer, buffer &indexBuffer,shaderResourceView &textureSRV,samplerState &textureSampler,const graphicsDefinitions::PRIMITIVE_TOPOLOGY &topology,const float4x4 &worldMatrix,const float4x4 &viewMatrix, const float4x4 &projMatrix,const float4 &color,const unsigned int &indexCount,const unsigned int &startIndexLocation,const int &baseVertexLocation);
		//uses basic shader to draw
		bool drawColorTexture(deviceContext &dContext,buffer &vertexBuffer,shaderResourceView &textureSRV,samplerState &textureSampler,const graphicsDefinitions::PRIMITIVE_TOPOLOGY &topology,const float4x4 &worldMatrix,const float4x4 &viewMatrix, const float4x4 &projMatrix,const float4 &color,const unsigned int &vertexCount,const unsigned int &startVertexLocation);
		//uses basic shader to draw
		template<typename T> bool drawColorTexture(deviceContext &dContext,const bucketList<T> &vertexData,shaderResourceView &textureSRV,samplerState &textureSampler,const graphicsDefinitions::PRIMITIVE_TOPOLOGY &topology,const float4x4 &worldMatrix,const float4x4 &viewMatrix, const float4x4 &projMatrix,const float4 &color)
		{
			buffer vertexBuffer;

			//creates vertex buffers
			if(!createVertexBuffers(dContext,vertexData,vertexBuffer))
				return false;

			//show the buffer
			return drawColorTexture(dContext,vertexBuffer,textureSRV,textureSampler,topology,worldMatrix,viewMatrix,projMatrix,color,vertexData.size(),0);
		}
		
		//uses basic shader to draw
		bool drawColorTexture2D(deviceContext &dContext,buffer &vertexBuffer,shaderResourceView &textureSRV,samplerState &textureSampler,const graphicsDefinitions::PRIMITIVE_TOPOLOGY &topology,const float4 &color,const unsigned int &vertexCount,const unsigned int &startVertexLocation);
		//uses basic shader to draw
		template<typename T> bool drawColorTexture2D(deviceContext &dContext,const bucketList<T> &vertexData,shaderResourceView &textureSRV,samplerState &textureSampler,const graphicsDefinitions::PRIMITIVE_TOPOLOGY &topology,const float4 &color)
		{
			buffer vertexBuffer;

			//creates vertex buffers
			if(!createVertexBuffers(dContext,vertexData,vertexBuffer))
				return false;

			//show the buffer
			return drawColorTexture2D(dContext,vertexBuffer,textureSRV,textureSampler,topology,color,vertexData.size(),0);
		}

		//uses basic shader to draw
		bool drawColorNormalTexture(deviceContext &dContext,buffer &vertexBuffer, buffer &indexBuffer,shaderResourceView &textureSRV,samplerState &textureSampler,const graphicsDefinitions::PRIMITIVE_TOPOLOGY &topology,const float4x4 &worldMatrix,const float4x4 &viewMatrix, const float4x4 &projMatrix,const float4 &color,const light &lightData,const unsigned int &indexCount,const unsigned int &startIndexLocation,const int &baseVertexLocation);
		//uses basic shader to draw
		bool drawColorNormalTexture(deviceContext &dContext,buffer &vertexBuffer,shaderResourceView &textureSRV,samplerState &textureSampler,const graphicsDefinitions::PRIMITIVE_TOPOLOGY &topology,const float4x4 &worldMatrix,const float4x4 &viewMatrix, const float4x4 &projMatrix,const float4 &color,const light &lightData,const unsigned int &vertexCount,const unsigned int &startVertexLocation);
		//uses basic shader to draw
		template<typename T> bool drawColorNormalTexture(deviceContext &dContext,const bucketList<T> &vertexData,shaderResourceView &textureSRV,samplerState &textureSampler,const graphicsDefinitions::PRIMITIVE_TOPOLOGY &topology,const float4x4 &worldMatrix,const float4x4 &viewMatrix, const float4x4 &projMatrix,const float4 &color,const light &lightData)
		{
			buffer vertexBuffer;

			//creates vertex buffers
			if(!createVertexBuffers(dContext,vertexData,vertexBuffer))
				return false;

			//show the buffer
			return drawColorNormalTexture(dContext,vertexBuffer,textureSRV,textureSampler,topology,worldMatrix,viewMatrix,projMatrix,color,lightData,vertexData.size(),0);
		}
		
		//prints text in the screen
		void print(deviceContext &dContext, const float2 &position,const char *string, ...);

		//draws the grid
		void showGrid(const camera &eye);
				
		//swaps buffer and checks for error
		inline void swapBuffer(void)
		{
			//swaps back and front buffer
			gl.swapBuffer();

			#ifdef _DEBUG
				//check for errors
				gl.checkError();
			#endif
		}		

		//returns default graphics
		inline graphics& getGraphics(void){return gl;};
		//returns default graphics device
		inline device& getDevice(void){return gDevice;};
		//returns default device context
		inline deviceContext& getDeviceContext(void){return dContext;};
		//returns window handle
		inline HWND& getWindowHandler(void){return gl.getWindowHandler();};
		//returns default render traget
		inline texture2D& getRenderTarget(void){return rTarget;};
		//returns default depth stencil
		inline texture2D& getDepthStencil(void){return dStencil;};
		//returns default render traget view
		inline renderTargetView& getRenderTargetView(void){return rTargetView;};
		//returns default depth stencil view
		inline depthStencilView & getDepthStencilView(void){return dStencilView;};

		//enables wireframe
		void setWireFrame(bool enabled);
		
		//frees the memory
		void clear(void);

		//destructor
		~graphicsUtility();

};


#endif